package com.easystore.orm.metadata
{

import com.easystore.orm.utilities.CheckUtil;
import com.easystore.orm.utilities.InstanceUtil;

import flash.utils.Dictionary;
import flash.utils.describeType;

import mx.collections.ArrayCollection;
	
public final class MetadataManager
{
	private static var __instance : MetadataManager;
	
	private var __mapping : Dictionary = new Dictionary(true);
	
	public function MetadataManager(internalClass : InternalClass) {
		CheckUtil.checkNotNull(internalClass,"internalClass", "MetadataManager.<constructor>" );
	}
	
	public static function getInstance() : MetadataManager {
		if (__instance == null ) __instance = new MetadataManager(new InternalClass());
		return __instance;
	}
	
	public function getSQLMetadata(classOrInstance : Object) : SQLMetadata {
		var sqlMetatada : SQLMetadata = classOrInstance is Class ? __mapping[classOrInstance] : __mapping[InstanceUtil.getClass(classOrInstance)];
		if ( sqlMetatada == null ) {
			loadMetadata(classOrInstance);
			return getSQLMetadata(classOrInstance);
		}
		return sqlMetatada;
	}	
	
	public function loadMetadata(classOrInstance : Object):void
	{
		CheckUtil.checkNotNull(classOrInstance,"classOrInstance", "MetadataManager.loadMetadata" );
		
		trace ( "creating metadata for class " + classOrInstance );
		
		var sqlMetatada : SQLMetadata = new SQLMetadata();
		
		var xml : XML = null ;
		if ( classOrInstance is Class ) {
			__mapping[classOrInstance] = sqlMetatada;
			xml = describeType( new classOrInstance() );
		} else {
			__mapping[InstanceUtil.getClass(classOrInstance)] = sqlMetatada;
			xml = describeType( classOrInstance );
		}
		// init vars
		var field : String;
		var column:String;
		var indexes : ColumnMapping = null;
		var whereCondition : String =" WHERE ";
		var insertParams:String ="";
		
		// init table
		sqlMetatada.tableName = xml.metadata.(@name=="Table").arg.(@key=="name").@value;
		trace ( " tableName = " + sqlMetatada.tableName );
		
		// init queries
		sqlMetatada.findAllQuery 		= "SELECT ";
		sqlMetatada.deleteQuery 		= "DELETE FROM " + sqlMetatada.tableName ; 
		sqlMetatada.insertQuery 		= "INSERT INTO " + sqlMetatada.tableName + " (" ;
		sqlMetatada.updateQuery 		= "UPDATE " + sqlMetatada.tableName + " SET ";
		sqlMetatada.tableQuery 			= "CREATE TABLE IF NOT EXISTS " + sqlMetatada.tableName + " (";
		sqlMetatada.uniqueIndexQuery 	= "CREATE UNIQUE INDEX " + sqlMetatada.tableName + "_index ON " + sqlMetatada.tableName + " (" ; 
		
		// init column mapping
		sqlMetatada.columnMappings = new ArrayCollection();
		sqlMetatada.indexMapping = new IndexMapping();
		
		var variables:XMLList = xml.accessor;
		var nbVariables : int = variables.length();
		var columnMapping : ColumnMapping;
		
		for (var i:int = 0 ; i < nbVariables ; i++) 
        {
        	field = variables[i].@name.toString();
			            	
        	if (variables[i].metadata.(@name=="Column").length()>0)
        	{
        		column = variables[i].metadata.(@name=="Column").arg.(@key=="name").@value.toString().toUpperCase(); 
        	} 
        	else
        	{
				column = field;
        	}
    		columnMapping = new ColumnMapping();
    		columnMapping.columnName = column;
    		columnMapping.propertyName = field;
			sqlMetatada.columnMappings.addItem(columnMapping);
			
			sqlMetatada.findAllQuery += column  +",";			
			
			
			trace ( " column  = " + column + " field "  + field );
			
        	if (variables[i].metadata.(@name=="Id").length()>0)
        	{
				indexes = new ColumnMapping();
				indexes.columnName = column;
				indexes.propertyName = field;
				trace ( " index  = " + column + " field "  + field );
				sqlMetatada.indexMapping.columnMappings.addItem(indexes);
        		whereCondition+= indexes.columnName + "=:" + indexes.propertyName + " AND ";
        		if ( variables[i].metadata.(@name=="AutoIncrement").length()>0 ) {
        			sqlMetatada.tableQuery += column + " INTEGER PRIMARY KEY AUTOINCREMENT,";
        		} else {
					sqlMetatada.tableQuery += column + " " + getSQLType(variables[i].@type) +",";        			
        		}
        		sqlMetatada.uniqueIndexQuery += column + ",";
        	} else {
        		sqlMetatada.insertQuery += column  +",";
        		insertParams += ":" + field  +",";
        		sqlMetatada.updateQuery += column + "=:" + field +",";
	        	sqlMetatada.tableQuery += column + " " + getSQLType(variables[i].@type) +",";
        	} 
        	
        }
        // remove last ',' character when needed
        sqlMetatada.updateQuery = sqlMetatada.updateQuery.substring(0, sqlMetatada.updateQuery.length -1);
        sqlMetatada.uniqueIndexQuery = sqlMetatada.uniqueIndexQuery.substring(0, sqlMetatada.uniqueIndexQuery.length -1);
        sqlMetatada.tableQuery = sqlMetatada.tableQuery.substring(0, sqlMetatada.tableQuery.length -1);
        sqlMetatada.insertQuery = sqlMetatada.insertQuery.substring(0, sqlMetatada.insertQuery.length -1);
        sqlMetatada.findAllQuery = sqlMetatada.findAllQuery.substring(0, sqlMetatada.findAllQuery.length -1);
        insertParams = insertParams.substring(0 , insertParams.length -1);
        
        // remove last ' AND ' character when needed
        whereCondition = whereCondition.substring(0, whereCondition.length -5);
        
        sqlMetatada.tableQuery += ")";
        sqlMetatada.deleteQuery += whereCondition;
        sqlMetatada.findAllQuery+= " FROM " + sqlMetatada.tableName;
        sqlMetatada.findQuery = sqlMetatada.findAllQuery + whereCondition;
        sqlMetatada.insertQuery += ") VALUES (" + insertParams + ")";
        sqlMetatada.updateQuery += whereCondition;
        sqlMetatada.uniqueIndexQuery += ")";
        
        trace ( "Queries : findAllQuery  = " + sqlMetatada.findAllQuery );
        trace ( "Queries : findQuery  = " + sqlMetatada.findQuery );
        trace ( "Queries : deleteQuery  = " + sqlMetatada.deleteQuery );
        trace ( "Queries : updateQuery  = " + sqlMetatada.updateQuery );
        trace ( "Queries : insertQuery  = " + sqlMetatada.insertQuery );
        trace ( "Queries : uniqueIndexQuery  = " + sqlMetatada.uniqueIndexQuery );
        trace ( "Queries : tableQuery  = " + sqlMetatada.tableQuery );
        
	}
	
	private function getSQLType(asType:String):String
	{
		if (asType == "int" || asType == "uint")
			return "INTEGER";
		else if (asType == "Number")
			return "REAL";
		else
			return "TEXT";				
	}
}
}
class InternalClass {}